package fr.babos.graphic.animations
{
	import com.greensock.easing.Quad;
	import com.greensock.TweenMax;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.PixelSnapping;
	import flash.display.StageQuality;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;
	import fr.babos.graphic.components.base.ResizableComponent;
	import fr.babos.utils.TimerUtils;
	
	/**
	 * @author ZoulouX
	 */
	public class BitmapSlideTransition implements ITransition
	{
		/**
		 * La durée de l'animation (en secondes)
		 */
		protected var _duration						:Number					= 2.3;
		
		/**
		 * Le easing
		 */
		protected var _ease							:Function;
		
		/**
		 * Le délais (en frames)
		 */
		protected var _delay						:uint;
		
		
		/**
		 * La durée de l'animation (en secondes)
		 */
		public function get duration ():Number { return _duration; }
		public function set duration (value:Number):void
		{
			_duration = value;
		}
		
		/**
		 * Le easing
		 */
		public function get ease ():Function { return _ease; }
		public function set ease (value:Function):void
		{
			_ease = value;
		}
		
		/**
		 * Le délais (en frames)
		 */
		public function get delay ():uint { return _delay; }
		public function set delay (value:uint):void
		{
			_delay = value;
		}
		
		
		/**
		 * Le constructeur
		 * @param	pEasing : Le easing (un default est utilisé si null)
		 * @param	pDuration : La durée de l'animation (en secondes)
		 * @param	pDelay : Le délais (en frames)
		 */
		public function BitmapSlideTransition (pEasing:Function = null, pDuration:Number = .3, pDelay:Number = 3)
		{
			_ease = (pEasing == null ? Quad.easeInOut : pEasing);
			_duration = pDuration;
			_delay = pDelay;
		}
		
		
		/**
		 * Récupérer la durée en frames
		 */
		protected function getFramesDuration (pTarget:ResizableComponent, pContextInfo:Object = null):int
		{
			return int((pContextInfo.duration ? pContextInfo.duration : _duration) * (pTarget.stage == null ? 30 : pTarget.stage.frameRate) + .5);
		}
		
		/**
		 * Convertir un composant en bitmap
		 */
		protected function drawBitmap (pComponent:ResizableComponent):Bitmap
		{
			// Créer le bitmap data aux dimensions du composant
			var bitmapData:BitmapData = new BitmapData(pComponent.width, pComponent.height, true, 0);
			
			// Dessiner le composant
			if ("drawWithQuality" in bitmapData)
				bitmapData.drawWithQuality(pComponent, null, null, null, null, false, StageQuality.HIGH);
			else
				bitmapData.draw(pComponent);
			
			// Créer et retourner le bitmap
			return new Bitmap(bitmapData, PixelSnapping.NEVER, false);
		}
		
		
		/**
		 * Animation d'intro
		 */
		public function playIn (pTarget:ResizableComponent, pContextInfo:Object = null, pStartHandler:Function = null, pStartHandlerParams:Array = null, pEndHandler:Function = null, pEndHandlerParams:Array = null):void
		{
			// Si on n'a pas de direction pour l'animation
			if (pContextInfo.direction == 0)
			{
				// Pas d'animation
				pEndHandler.apply(null, pEndHandlerParams);
				return;
			}
			
			// Masquer la cible
			pTarget.visible = false;
			
			// Attendre une frame
			TimerUtils.wait(pTarget, 1, true, function ():void
			{
				// Afficher la cible
				pTarget.visible = true;
				
				// L'objet à bouger selon le contexte
				var moveTarget:DisplayObject;
				var moveProperty:String;
				
				// Si on nous interdit d'utiliser un bitmap pour la transition
				if ("useBitmap" in pContextInfo && !pContextInfo.useBitmap)
				{
					// La cible est l'objet, directement
					moveTarget = pTarget;
					moveProperty = "horizontalOffset";
				}
				else
				{
					// Convertir l'objet en bitmap et cibler ce bitmap
					moveTarget = drawBitmap(pTarget);
					moveProperty = "x";
					
					// Afficher le bitmap
					pTarget.parent.addChild(moveTarget);
					
					// Placer le bitmap
					moveTarget.x = pTarget.x;
					moveTarget.y = pTarget.y;
					
					// Masquer l'élément
					pTarget.alpha = 0;
				}
				
				// La destination
				var destination:Number = pTarget.horizontalOffset;
				
				// Signaler que l'animation démarre
				if (pStartHandler != null)
					pStartHandler.apply(null, pStartHandlerParams);
				
				// Placer la vue hors champ
				if ("amplitude" in pContextInfo && pContextInfo.amplitude != -1)
					moveTarget[moveProperty] += pContextInfo.amplitude;
				else
					moveTarget[moveProperty] += pTarget.totalWidth;
				
				// La direction
				moveTarget[moveProperty] = moveTarget[moveProperty] * (pContextInfo.direction ? pContextInfo.direction : 1);
				
				// Si on doit jouer sur l'alpha
				if ("useAlpha" in pContextInfo && pContextInfo.useAlpha)
					moveTarget.alpha = 0;
				
				// Les paramètres de la tween
				var tweenObject:Object = {
					ease: _ease,
					delay: _delay,
					alpha: moveTarget.alpha == 0 ? 1 : null,
					useFrames: true,
					onComplete: animationEndHandler,
					onCompleteParams: [true, pTarget, moveTarget, pEndHandler, pEndHandlerParams]
				}
				
				// Appliquer la destination selon la propriété
				tweenObject[moveProperty] = destination;
				
				// Animer
				TweenMax.to(moveTarget, getFramesDuration(pTarget, pContextInfo), tweenObject);
			});
		}
		
		/**
		 * Animation de sortie
		 */
		public function playOut (pTarget:ResizableComponent, pContextInfo:Object = null, pStartHandler:Function = null, pStartHandlerParams:Array = null, pEndHandler:Function = null, pEndHandlerParams:Array = null):void
		{
			// Si on n'a pas de direction pour l'animation
			if (pContextInfo.direction == 0)
			{
				pEndHandler.apply(null, pEndHandlerParams);
				return;
			}
			
			// Attendre une frame
			TimerUtils.wait(pTarget, 1, true, function ():void
			{
				// L'objet à bouger selon le contexte
				var moveTarget:DisplayObject;
				var moveProperty:String;
				
				// Si on nous interdit d'utiliser un bitmap pour la transition
				if ("useBitmap" in pContextInfo && !pContextInfo.useBitmap)
				{
					// La cible est l'objet, directement
					moveTarget = pTarget;
					moveProperty = "horizontalOffset";
				}
				else
				{
					// Convertir l'objet en bitmap et cibler ce bitmap
					moveTarget = drawBitmap(pTarget);
					moveProperty = "x";
					
					// Afficher le bitmap
					pTarget.parent.addChild(moveTarget);
					
					// Placer le bitmap
					moveTarget.x = pTarget.x;
					moveTarget.y = pTarget.y;
					
					// Masquer l'élément
					pTarget.alpha = 0;
				}
				
				// Signaler que l'animation démarre
				if (pStartHandler != null)
					pStartHandler.apply(null, pStartHandlerParams);
				
				// La destination
				var destination:Number = pTarget.horizontalOffset;
				
				// La destination hors champs
				if ("amplitude" in pContextInfo && pContextInfo.amplitude != -1)
					destination = pContextInfo.amplitude;
				else
					destination = - pTarget.totalWidth;
				
				// La direction
				destination = destination * (pContextInfo.direction ? pContextInfo.direction : 1);
				
				// Les paramètres de la tween
				var tweenObject:Object = {
					ease: _ease,
					delay: _delay,
					alpha: "useAlpha" in pContextInfo && pContextInfo.useAlpha ? 0 : null,
					useFrames: true,
					onComplete: animationEndHandler,
					onCompleteParams: [false, pTarget, moveTarget, pEndHandler, pEndHandlerParams]
				}
				
				// Appliquer la destination selon la propriété
				tweenObject[moveProperty] = destination;
				
				// Animer
				TweenMax.to(moveTarget, getFramesDuration(pTarget, pContextInfo), tweenObject);
			});
		}
		
		/**
		 * Fin d'une animation
		 */
		protected function animationEndHandler (pPlayIn:Boolean, pTarget:ResizableComponent, pMoveTarget:DisplayObject, pEndHandler:Function, pEndHandlerParams:Array):void
		{
			// Si on est en playIn
			if (pPlayIn)
				pTarget.alpha = 1;
			
			// Si on déplaçait un bitmap
			if (pMoveTarget is Bitmap)
			{
				// On le vire
				pMoveTarget.parent.removeChild(pMoveTarget);
				(pMoveTarget as Bitmap).bitmapData.dispose();
			}
			
			// Attendre pour appeler le handler
			TimerUtils.wait(pTarget, _delay, true, pEndHandler, pEndHandlerParams);
		}
	}
}